
/*
 *  Copyright 2007 Marc Lanctot and Marc Gendron-Bellemare
 *
 *  This file is part of OpenMTG.
 *
 *  OpenMTG is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenMTG is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenMTG.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

import java.io.*;

public class SimpleTextUI extends UI
{
  public SimpleTextUI(Client client)
  {
    super(client);
  }
  
  public SimpleTextUI(Client client, GameState state, Player me)
  {
    super(client, state, me);
  }

  public void run()
  {
    //System.out.println("Sent player and deck info to server...");
    boolean gameStarted = false;

    try {

      String userInput = "";
    
      BufferedReader stdIn = new BufferedReader(
        new InputStreamReader(System.in));

      while (!(userInput = stdIn.readLine()).equals("quit")) {
        if (userInput == null)
          break;

        handleInput(userInput.trim()); 
        //out.println(userInput);
        //System.out.println("echo: " + in.readLine());
      }
    }
    catch(Exception e) { throw new RuntimeException(e); }
  }

  private void showPlayerField(int id)
  {
    if (UI.color)
      showPlayerFieldColor(id);
    else
      showPlayerFieldNoColor(id);
  }
  
  private void showPlayerFieldNoColor(int id)
  {
    showPlayerField(id, "", "", "", false);
  }
  
  private void showPlayerFieldColor(int id)
  {
    showPlayerField(id, Constants.CLR_BRMAGENTA, Constants.CLR_BRCYAN, Constants.CLR_END, true);
  }
  
  private void showPlayerField(int id, String clr_status, String clr_sep, String clr_end, boolean color)
  {
    Player p = state.getPlayer(id);
        
    int deckSize = p.libsize;
    
    System.out.println(clr_status + p.getName() + " (id " + p.getId() + ")" 
                       + "  LP: " + p.getLifePoints() 
                       + "  LY/GY: " + deckSize + "/" + p.aGraveyard.size() + " cards"
                       + "" + clr_end);

    if (p == me) {
      System.out.println(clr_sep + "  Hand:" + clr_end);

      for (Card c : p.getHand())
        System.out.println((color ? Helpers.getColor(c) : "") + "   " + c.toString() + clr_end); 

    }
    else {
      System.out.println(clr_sep + "  Hand: " + p.getHandSize() + " Cards" + clr_end);
    }

    // mana pool

    if (p.manaPool.length() > 0)
      System.out.println(clr_sep + "  Mana: " + (color ? Helpers.colorizeManaPool(p.manaPool) : p.manaPool) + clr_end);

    // permanents
      
    // lands
    if (p.lands.size() > 0) {
      System.out.println(clr_sep + "  Lands:" + clr_end);

      for (Card c : p.lands)
        System.out.println((color ? Helpers.getColor(c) : "") + "   " + c.toString() + clr_end); 
    }

    // creatures
    if (p.creatures.size() > 0) {
      System.out.println(clr_sep + "  Creatures:" + clr_end);

      for (Card c : p.creatures)
        System.out.println((color ? Helpers.getColor(c) : "") + "   " + c.toString() + clr_end); 
    }

    // artifacts
    // enchantments

    System.out.println("");
  }
  
  private void showState() 
  {
    System.out.println("");

    // show turn number and who's turn it is

    // show player fields
    for (int i = 0; i < state.getNumPlayers(); i++)
      if (i != me.getId())
        showPlayerField(i);

    showPlayerField(me.getId());
  }

  public void showMsg(String msg)
  {
    System.out.println("> " + msg);
  }
  
  // network event handler

  public void handleEvent(String line)
  {
    String[] parts = line.split(" "); 
    
    //if (parts.length < 1)
    //  error
    
    String token = parts[0];    
    
    if (token.equals("OK"))      
      handleOK(line);
    else if (token.equals("GAMESTART"))
      handleGameStart(line);
    else if (token.equals("PLAYERSTART"))
      handlePlayerStart(line); 
    else if (token.equals("MULLIGAN"))
      handleMulligan(line); 
    else if (token.equals("STARTTURN"))
      handleStartTurn(line); 
    else if (token.equals("PLAYLAND"))
      handlePlayLand(line); 
    else if (token.equals("TAPFORMANA"))
      handleTapForMana(line); 
    //else if (token.equals("CAST"))
    //  handleCast(line); 
    else if (token.equals("ATTACK"))
      handleAttack(line); 
    else if (token.equals("ERROR"))
      handleError(line); 
    else if (token.equals("D"))
      handleNetworkD(line); 
		else if (token.equals("QUERYDISCARD"))
			handleQueryDiscard(line);
    else if (token.equals("PHASE"))
      handlePhase(line);
    else if (token.equals("MSG"))
      handleMsg(line);
  }

  private void handleError(String line) {
    String error = line.substring(6); 
    
    if (error.equals("Too many players")) {
      System.out.println("Error: this game is already full.");
      System.exit(-1);
    }
    else if (error.equals("Duplicate name")) {
      System.out.println("Error: the name you chose is in use; please choose another.");
      System.exit(-1);
    }
    
    System.out.println("Error: " + error); 
  }
  
  private void handleOK(String line) {
    String[] parts = line.split(" ");
    if (parts[1].equals("deckinfo"))
      showMsg("Waiting for other players ...");
  }

  private void handleGameStart(String line)
  {
    String[] parts = line.split(" "); 
    int idToDecide = Integer.parseInt(parts[1]);

    //showState(); 

    if (idToDecide == me.getId())
    {
      client.phase = 1;
      System.out.println("> You are to decide who starts.");
      System.out.println("> Enter 's' for start, 'o' for opponent to start. ");
    }
    else
    {
      client.phase = 2;
      System.out.println("> Opponent is deciding who starts. ");
    }
  }

  private void handlePlayerStart(String line) {
    String[] parts = line.split(" "); 
    int idToStart = Integer.parseInt(parts[1]);

    if (idToStart == me.getId()) {
      client.phase = 3;

      showState();

      System.out.println("> You will be taking the first turn. "); 
      System.out.println("> Enter 'm' for mulligan, or 's' to start game.");
    }
    else {
      client.phase = 4; 

      showState(); 

      System.out.println("> Your opponent will be taking the first turn. "); 
      System.out.println("> Enter 'm' for mulligan, or 's' to start game.");
    }

  }
  
  private void handleMulligan(String inputLine) {
    String[] parts = inputLine.split(" ");
    int code = Integer.parseInt(parts[1]); 

    if (code == 2)
    {
      showState(); 
      System.out.println("> Enter 'm' for mulligan, or 's' to start game.");
    }
  }

  private void handleStartTurn(String inputLine) {
    System.out.println();
    if (client.turnPlayerId == me.getId())
    {
      System.out.println("> It is now your turn. Type 'help' for help with commands.");
    }
    else
    {
      Player p = state.getPlayer(client.turnPlayerId); 
      System.out.println("> It is now " + p.getName() + "'s turn. Type 'help' for help with commands"); 
    }
  }
   
  private void handleTapForMana(String inputLine) {
    String[] parts = inputLine.split(" ");
    int pid = Integer.parseInt(parts[1]); 

		// First check whether land was successfully tapped
		if (parts.length < 2) // Only command & pid
			return;

    if (pid == me.getId()) {
      System.out.println("> You tapped land for mana. Your mana pool is now " + me.manaPool); 
    }
    else {
      Player p = state.getPlayer(pid); 
      System.out.println("> " + p.getName() + " tapped land for mana. Their mana pool is now " + p.manaPool); 
    }
  }

  private void handlePlayLand(String inputLine) {
    String[] parts = inputLine.split(" ");

    int pid = Integer.parseInt(parts[1]); 
  
    CardLoader cl = new CardLoader();
    Card c = cl.decode(parts[2]);

    Player p = state.getPlayer(pid); 

    String name = "You";
    if (p.getId() != me.getId())
      name = p.getName();

    System.out.println("> " + name + " played a land: " + c.getName());
  }

  /*
   * Handled by Client at the moment
  private void handleCast(String inputLine) {
    String[] parts = inputLine.split(" ");

    int pid = Integer.parseInt(parts[1]); 
  
    CardLoader cl = new CardLoader();
    Card c = cl.decode(parts[2]);

    Player p = state.getPlayer(pid); 

    String name = "You";
    if (p.getId() != me.getId())
      name = p.getName();

    String type = "";
    if (c.getType() == Card.TYPE_CREATURE)
      type = "creature";

    System.out.println("> " + name + " cast a " + type + ": " + c.getName());
  }
  */

  private void handleAttack(String inputLine) {
    String[] parts = inputLine.split(" ");

    int a_pid = Integer.parseInt(parts[1]); 
    int d_pid = Integer.parseInt(parts[2]); 
  
    Player ap = state.getPlayer(a_pid); 
    Player dp = state.getPlayer(d_pid); 

    String attackerName = "You are";
    if (ap.getId() != me.getId())
      attackerName = (ap.getName() + " is");

    String defenderName = "you";
    if (dp.getId() != me.getId())
      defenderName = dp.getName();

    for (int i = 3; i < parts.length; i++) {
      int iid = Integer.parseInt(parts[i]); 
      Card c = ap.getCardInCreatures(iid); 

      System.out.println("> " + attackerName + " attacking " + defenderName + " with " + c.toString());
    }
  }
	
	private void handleQueryDiscard(String input)
	{
		String[] args = input.split(" ",2);
		System.out.println("> You must discard "+args[1]+" cards.");
	}
	
  private void handleNetworkD(String inputLine) {
    showMsg("Ok. Waiting on other players.");
  }
    
  private void handlePhase(String inputLine) {
    String[] args = inputLine.split(" ", 2);

    String phaseName;
    
    try
    {
      phaseName = PhaseInfo.Phase.valueOf(args[1]).longName();
    }
    catch (IllegalArgumentException e)
    {
      phaseName = "Unknown ("+args[1]+")";
    }

    System.out.println ("> New phase: "+phaseName);
  }
  
  public void handleMsg(String inputLine)
  {
    String[] args = inputLine.split(" ", 2);
    System.out.println ("> "+args[1]);
  }

  // ----------------------------------------------------------------------------
  // input handlers
  
  private void handleInput(String input)
  {
    if (client.phase >= 5) {
      parseTurnInput(input);
      return;
    }

    if (client.phase == 1 && input.equals("s"))
    {
      client.send("GAMESTART " + me.getId()); 
      client.phase = 3;
    }
    else if (client.phase == 1 && input.equals("o"))
    {
      client.send("GAMESTART -1"); // not me
      client.phase = 4;
    }
    else if (   (client.phase == 3 && input.equals("m"))
             || (client.phase == 4 && input.equals("m")) )
    {
      client.send("MULLIGAN");
    }
    else if (   (client.phase == 3 && input.equals("s"))
             || (client.phase == 4 && input.equals("s")) )
    {
      client.send("FIRSTTURN");
    }
  }

  private void printHelp() {
    // IMPORTANT: let's keep this at 80 chars / line
    
    System.out.println("");
    System.out.println("Valid turn commands. Arguments are delimited with spaces.");
    System.out.println("   a <ids..>                        attack (in 2-player mode)");
    System.out.println("   a <player id> <ids..>            attack (in multiplayer mode)");
    System.out.println("   ad <to id> <dmg> [from id]       assign damage to creature");
    System.out.println("   b <attacker id> <ids..>          block");
    System.out.println("   c <id> [tids..]                  cast, tid = target id");
    System.out.println("   d                                end spell/effect stack (resolve stack)");
    System.out.println("   pl <id>                          play land");
    System.out.println("   show <ids..>                     show player field (no args shows all)"); 
    System.out.println("   tlm <ids..>                      tap land(s) for mana"); 
		System.out.println("   di <ids..>                       discard cards, when requested");
    System.out.println("   go                               proceed to next phase");
    System.out.println("");
		System.out.println("  Whenever the system believes you could take an action (declaring attackers, \n" +
				               "  blockers, casting sorceries), it will give you the opportunity to do so. To \n" +
				               "  proceed, you must type 'go'. If an action is required - e.g., discarding -, \n" +
				               "  then use the appropriate command.");
		System.out.println("  Example: In the main phase before combat, Joe casts Savannah Lions. After\n" +
				               "  resolving the cast stack, he should send 'go' to indicate that he is done\n" +
				               "  with this phase.");
		System.out.println("  For now, you will have to look at the PHASE information to determine whether\n" +
				               "  you need to go. When in doubt, send 'go' - it will return an error if this is\n" +
				               "  not the correct action.");
		System.out.println("");
  }

  private void parseTurnInput(String input)
  {
    String[] parts = input.split(" "); 
    
    //if (parts.length < 1)
    //  error
    
    String token = parts[0];
    
    if (token.equals("help"))
      printHelp();
    else if (token.equals("tlm"))
      handleTLM(input);
    else if (token.equals("show"))
      handleShow(input);
    else if (token.equals("pl"))
      handlePL(input);
    else if (token.equals("c"))
      handleC(input);
    else if (token.equals("a"))
      handleA(input);
    else if (token.equals("b"))
      handleB(input);
    else if (token.equals("d"))
      handleD(input);
    else if (token.equals("go"))
      handleGO(input);
    else if (token.equals("di"))
      handleDI(input);
    else if (token.equals("ad"))
      handleAD(input);
    else if (input.equals(""))
      handleEnter();
  }

  private void handlePL(String input)
  {
		String[] arg = input.split(" ");
		// Make sure we have an argument, & it is an integer
		if (arg.length < 2 || !isNumeric(arg[1]))
		{
			System.out.println("Which land do you wish to play?");
			return;
    }
			client.send("PLAYLAND " + arg[1]);
  }
  
  private void handleShow(String input)
  {
    String[] parts = input.split(" ");

    if (parts.length == 1) {
      showState();
      return;
    }

    for (int i = 1; i < parts.length; i++)
    {
      int id = Integer.parseInt(parts[i]);
      showPlayerField(id);
    }
      
  }

  private void handleTLM(String input)
  {
		String[] arg = input.split(" ", 2);
		// Make sure we have an argument, & it is an integer
		if (arg.length < 2)
		{
			System.out.println ("Which land(s) do you wish to tap?");
			return;
    }
			client.send("TAPFORMANA " + arg[1]);
  }
   
  // right now only supports casting creatures (no targets)
  private void handleC(String input)
  {
		String[] arg = input.split(" ", 2);
		if (arg.length < 2 || !isNumeric(arg[1]))
		{
			System.out.println ("Which spell do you wish to cast?");
			return;
    }
			client.send("CAST " + arg[1]);
  }
  
  private void handleA(String input)
  {
		String[] arg = input.split(" ");
		if (arg.length < 2 || !isNumeric(arg[1]))
		{
			System.out.println ("Attack what?");
			return;
    }
		
		int index = input.indexOf(arg[1]);
		String remainder = input.substring(index); 
		
			client.send("ATTACK " + remainder);
  }
  
  private void handleB(String input)
  {
		String[] arg = input.split(" ");
		// @@@ fixme - check for all isNumeric
		if (arg.length < 3 || !isNumeric(arg[1]) || !isNumeric(arg[2]))
		{
			System.out.println ("Block how?");
			return;
    }
			client.send("BLOCK " + arg[1] + " " + arg[2]);
  }
  
  private void handleGO(String input)
  {
    client.send("GO");
  }

	private void handleDI(String input)
	{
		String[] args = input.split(" ");
		boolean bad = false;

		String line = "";
		
		if (args.length < 2) bad = true;
		for (int i = 1; i < args.length; i++)
			if (!isNumeric(args[i]))
			{
				bad = true;
				break;
			}
			else
				line += args[i]+" ";
		
		if (bad)
		{
			System.out.println ("Discard which cards?");
			return;
    }
		else
			client.send("DISCARD "+line);
	}
	
  private void handleD(String input)
  {
    client.send("D");
  }  

  private void handleEnter()
  {
    client.send("NEXT");
  }

  private void handleAD(String input)
  {
    String[] args = input.split(" ");
    boolean bad = false;

    // input format: ad <to id> <dmg> [from id]
    // format sent to server: ad <caller player id> <to id> <dmg> [from id]

    String line = "" + me.getId() + " ";
    
    if (args.length < 3) bad = true;
    for (int i = 1; i < args.length; i++)
      if (!isNumeric(args[i]))
      {
        bad = true;
        break;
      }
      else
        line += args[i]+" ";
    
    if (bad)
    {
      System.out.println ("Assign damage to who? (from who?)");
      return;
    }
    else
      client.send("AD "+line);
  }    
  
	protected boolean isNumeric(String pString)
	{
		// Try to parse it...
		try
		{
			Integer.parseInt(pString);
		}
		catch (NumberFormatException e)
		{
			return false;	
		}

		return true;
	}
}


